// 客户端运行所需的相关配置ke
package config

import (
	"doveclient/encrypt"
	"doveclient/util"
	"errors"
	"fmt"
	"os"
	"path"
	"reflect"
	"runtime"
	"strings"
	"time"
	"git.oschina.net/chaos.su/go-toml"
)

const (
	MAX_CONNECTIONS = 15000
	// 客户端响应超时
	CLIENT_TIMEOUT  = time.Second * 5
	CLIENT_MAX_IDLE = time.Second * 120
	// 请求处理超时
	PROCESS_TIMEOUT = int64(10)
)

var configFile string

type Config struct {
	ListenAddr       string
	ListenAddrTransi string
	RunAsBi          bool
	InstallDir       string
	PidFile          string

	DATA_ROOT             string
	DATA_DIR              string
	LOG_DIR               string
	CONFIG_DIR            []string
	DAEMON_USER           string
	DAEMON_UID            int
	DAEMON_GID            int
	POST_UPDATE_CALLBACKS []string
	POST_UPDATE_SCRIPTS   []string
	ENV                   string
	ClientVer             string
	ENCRYPT_ENABLE        bool
	ENCRYPT_KEY           string
	ENCRYPT_IV            string
	ENCRYPT_VER           float64
	DEVMODE               bool
}

var config = &Config{}

//zk的配置暂单独设置，不由运维修改
var zkConfig *map[string]interface{}

var debug bool

//	TODO: not coroutine safe
func DebugEnabled() bool {
	return debug
}

//	TODO: not coroutine safe
func SetDebug(enable bool) {
	debug = enable
}

func SetConfigFile(f string) {
	configFile = f
}

//	TODO: not coroutine safe
func SetConfig(name string, val interface{}) {
	reflect.ValueOf(config).Elem().FieldByName(name).Set(reflect.ValueOf(val))
}

func GetBiPidFile() string {
	return config.DATA_ROOT + "bi.pid"
}
func Setup() error {
	var err error
	// 安全相关的配置文件
	secretConfig, err = ParseSecretSettings()
	if err != nil {
		return errors.New(fmt.Sprintf("Failed to parse secret configs: %s ", err))
	}

	// 获取并解析配置文件/etc/DoveClient.conf,其中的配置具有优先级
	config, err = ParseExtraConfig()
	if err != nil {
		return errors.New(fmt.Sprintf("Failed to initialize configs: %s ", err))
	}

	config.ClientVer = secretConfig.Get("Ver").(string)

	// 检查配置文件里设置的环境标识是否正确
	if err = func() error {
		if secretConfig.Get("Zookeeper.host."+config.ENV) == nil {
			return fmt.Errorf("Zookeeper.host."+config.ENV + "not found.")
		}
		if secretConfig.Get("Encrypt."+config.ENV) == nil {
			return errors.New("Encrypt."+config.ENV + "not found.")
		}
		return nil
	}(); err != nil {
		envs := secretConfig.Get("Zookeeper.host").(*toml.TomlTree).Keys()
		return fmt.Errorf("env [%s] has not been suppored: %v, valid envs are %+v", config.ENV, err, envs)
	}
	config.DEVMODE = strings.Index(config.ENV, "DEV") == 0

	var daemonUid, daemonGid int
	if config.DAEMON_USER == "" {
		daemonGid = os.Getegid()
		daemonUid = os.Getuid()
	} else {
		daemonUid, daemonGid, err = LookupOsUidGid(config.DAEMON_USER)
	}

	if err != nil {
		return err
	}

	config.DAEMON_UID = daemonUid
	config.DAEMON_GID = daemonGid

	// 默认配置检测及设置
	switch runtime.GOOS {
	case "linux", "darwin":
		if config.DATA_DIR == "" {
			config.DATA_ROOT = "/var/lib/doveclient/"
		}

	case "windows":
		if config.DATA_DIR == "" {
			config.DATA_ROOT = os.Getenv("APPDATA") + "\\doveclient\\"
		}
	}

	config.DATA_ROOT = strings.TrimRight(config.DATA_DIR, string(os.PathSeparator)) + string(os.PathSeparator)

	if !util.FileExists(config.DATA_ROOT) {
		err = os.MkdirAll(config.DATA_ROOT, 0755)
		if err != nil {
			return errors.New(fmt.Sprintf("Failed to initialize data root dir[%s]: %s", config.DATA_ROOT, err))
		}
	}
	err = Rchown(config.DATA_ROOT, daemonUid, daemonGid)

	if err != nil {
		return errors.New(fmt.Sprintf("Failed to change data root dir owner: %s", err))
	}

	if config.ListenAddr == "" {
		switch runtime.GOOS {
		case "windows":
			config.ListenAddr = "127.0.0.1:4510"
			config.ListenAddrTransi = "127.0.0.1:4511"
		default:
			config.ListenAddr = config.DATA_DIR + "doveclient.sock"
			config.ListenAddrTransi = config.DATA_DIR + "doveclient.transi.sock"
		}
	}

	config.InstallDir = path.Dir(os.Args[0]) + string(os.PathListSeparator)
	if !config.RunAsBi {
		config.PidFile = config.DATA_ROOT + "pid"
	} else {
		config.PidFile = GetBiPidFile()
	}
	config.DATA_DIR = config.DATA_ROOT + string(os.PathSeparator) + config.ENV + string(os.PathSeparator)
	if config.ENCRYPT_IV == "" {
		config.ENCRYPT_IV = secretConfig.Get("Encrypt." + config.ENV + ".iv").(string)
	}
	if config.ENCRYPT_KEY == "" {
		config.ENCRYPT_KEY = secretConfig.Get("Encrypt." + config.ENV + ".key").(string)
	}
	if config.ENCRYPT_VER == 0 {
		config.ENCRYPT_VER = secretConfig.Get("Encrypt.Ver").(float64)
	}

	if len(config.ENCRYPT_IV) != encrypt.ENC_IV_SIZE {
		return errors.New(fmt.Sprintf("Config encryption enabled, but ENCRYPT_IV  defined in config is not valid. Expecting a %d-byte IV, but %d bytes detected!", encrypt.ENC_IV_SIZE, len(config.ENCRYPT_IV)))
	}

	if len(config.ENCRYPT_KEY) < 1 {
		return errors.New("Config encryption enabled, but ENCRYPT_KEY length is shorter than 1")
	}
	if !util.FileExists(config.DATA_DIR) {
		err = os.MkdirAll(config.DATA_DIR, 0755)
		if err != nil {
			return errors.New(fmt.Sprintf("Failed to initialize data dir[%s]: %s", config.DATA_DIR, err))
		}
	}

	err = Rchown(config.DATA_DIR, daemonUid, daemonGid)

	if err != nil {
		return errors.New(fmt.Sprintf("Failed to change data dir owner: %s", err))
	}

	if !util.FileExists(config.LOG_DIR) {
		err = os.MkdirAll(config.LOG_DIR, 0755)
		if err != nil {
			return errors.New(fmt.Sprintf("Failed to initialize log dir[%s]: %s", config.LOG_DIR, err))
		}
	}

	err = Rchown(config.LOG_DIR, daemonUid, daemonGid)
	if err != nil {
		return errors.New(fmt.Sprintf("Failed to change log dir owner: %s", err))
	}
	initZkConfig()
	return nil
}
